/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ifs.RnD.revisionRead;

/**
 *
 * @author amiplk
 */
import ifs.RnD.Graph.MergeEdge;
import ifs.RnD.Graph.RevVertex;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import ifs.RnD.Graph.Vertex;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

/**
 * This class draws the revision graph
 * revisions are represented in JLabels with different shapes for different revision types 
 */
public final class RevisionGraph extends javax.swing.JFrame {

    DisplayReposiroryHistory repoHis;
    private Dimension area; //indicates area taken up by graphics
    RevVertex u;
    ArrayList<Revision> rangeRevObj;
    RevisionGraph frame;
    RevRange revRange;
    public boolean tagShow;
    /**
     * Label which shows the url of the selected node
     */
    public JLabel nodeUrl = new JLabel();
    List<RevVertex> list;
    int size = 0;
    private Vector<Vertex> circles1;
    private JPanel drawingPane;
    int noEdges = 0;
    RevVertex center;
    RevVertex first;
    RevVertex second;
    RevVertex third;
    RevVertex tag;
    int x;
    int y;
    int sW1;
    int sW2;
    int r = 0;
    static int i;
    int xSize;
    int ySize;
    int px;
    int py;
    final int W = 100;
    final int H = 100;
    boolean changed;
    int revSize;
    MergeEdge prevpoint;
    ArrayList<Revision> revisionObjects;
    ArrayList<Revision> tagObjects;
    Map revNtag;
    static boolean sts;
    ArrayList<RevVertex> V;
    RevVertex TnV[] = new RevVertex[1000];
    ArrayList<MergeEdge> mergeedgeArrayL = new ArrayList<MergeEdge>();
    ArrayList<MergeEdge> edgeArrayL = new ArrayList<MergeEdge>();
    Graphics graphics;
    java.awt.MenuBar mainMenuBar;
    java.awt.Menu menu1;
    java.awt.MenuItem miNew;
    java.awt.MenuItem miOpen;
    java.awt.MenuItem miSave;
    java.awt.MenuItem miSaveAs;
    java.awt.MenuItem miExit;
    java.awt.Menu menu2;
    java.awt.MenuItem miCut;
    java.awt.MenuItem miCopy;
    java.awt.Menu menu3;
    java.awt.MenuItem miAbout;
    javax.swing.JToolBar toolBar1;
    javax.swing.JButton button1;
    javax.swing.JButton button2;
    javax.swing.JLabel bURL;
    javax.swing.JLabel bText;
    javax.swing.JLabel space2;
    javax.swing.JLabel space3;
    javax.swing.JLabel space4;
    javax.swing.JLabel space5;
    javax.swing.JLabel space10;
    javax.swing.JLabel space11;
    javax.swing.JLabel purpleLbl;
    javax.swing.JLabel greenLbl;
    javax.swing.JLabel yelowLbl;
    javax.swing.JLabel maroonLbl;
    javax.swing.JLabel space15;
    javax.swing.JLabel space16;
    javax.swing.JLabel space17;
    javax.swing.JLabel space18;
    javax.swing.JLabel space19;
    javax.swing.JLabel space20;
    javax.swing.JSeparator sep3;
    javax.swing.JButton full;
    javax.swing.JButton filter;
    javax.swing.JButton expandB;
    javax.swing.JButton compressB;
    javax.swing.ImageIcon filterImg;
    javax.swing.ImageIcon fullImg;
    javax.swing.ImageIcon tagImg;
    javax.swing.ImageIcon expandImg;
    javax.swing.ImageIcon compressImg;
    javax.swing.ImageIcon yelow;
    javax.swing.ImageIcon green;
    javax.swing.ImageIcon purple;
    javax.swing.ImageIcon maroon;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JButton jButton10;
    private javax.swing.JButton jButton11;
    private javax.swing.JButton jButton12;
    private javax.swing.JButton jButton13;
    private javax.swing.JButton jButton14;
    private javax.swing.JButton ExpandB;
    private javax.swing.JButton CompressB;
    private javax.swing.JButton FilterB;
    private javax.swing.JButton FullB;
    private javax.swing.JButton HelpB;
    int ny;
    int a = 0;
    String rootUrl;
    String recUrl1;
    javax.swing.JToolBar vertical;
    public javax.swing.JLabel statusbar;

    private void initComponents() {

        addWindowListener(new java.awt.event.WindowAdapter() {

            @Override
            public void windowClosing(java.awt.event.WindowEvent evt) {
            }
        });

        jPanel4 = new javax.swing.JPanel();
        ExpandB = new javax.swing.JButton();
        CompressB = new javax.swing.JButton();
        FilterB = new javax.swing.JButton();
        FullB = new javax.swing.JButton();
        HelpB = new javax.swing.JButton();
        mainMenuBar = new java.awt.MenuBar();
        menu1 = new java.awt.Menu("File");
        miNew = new java.awt.MenuItem("New");
        menu1.add(miNew);
        menu1.addSeparator();
        miSave = new java.awt.MenuItem("Edit");
        menu1.add(miSave);
        miSaveAs = new java.awt.MenuItem("Delete");
        menu1.add(miSaveAs);
        menu1.addSeparator();
        miExit = new java.awt.MenuItem("Exit    ALT+F4");
        menu1.add(miExit);
        mainMenuBar.add(menu1);
        menu2 = new java.awt.Menu("Edit");
        miCut = new java.awt.MenuItem("Final");
        menu2.add(miCut);
        miCopy = new java.awt.MenuItem("Normal");
        menu2.add(miCopy);
        mainMenuBar.add(menu2);
        menu3 = new java.awt.Menu("Help");
        mainMenuBar.setHelpMenu(menu3);
        miAbout = new java.awt.MenuItem("About");
        menu3.add(miAbout);
        mainMenuBar.add(menu3);
        toolBar1 = new javax.swing.JToolBar("");
        bURL = new javax.swing.JLabel("              URL : ");
        bURL.setFont(new Font("Serif", Font.BOLD, 12));
        bText = new javax.swing.JLabel();
        sep3 = new javax.swing.JSeparator();


        jPanel4.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
        jPanel4.setPreferredSize(new java.awt.Dimension(140, 470));

        ExpandB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ifs/RnD/Resources/expandA1.jpg")));
        ExpandB.setToolTipText("Expand Graph");
        ExpandB.setPreferredSize(new java.awt.Dimension(70, 70));
        ExpandB.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                test(revisionObjects, false, frame);
            }
        });

        CompressB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ifs/RnD/Resources/compressA1.jpg")));
        CompressB.setToolTipText("Compress Graph");
        CompressB.setPreferredSize(new java.awt.Dimension(70, 70));
        CompressB.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                test(revisionObjects, true, frame);
            }
        });
        FilterB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ifs/RnD/Resources/data_filter1.jpg"))); // NOI18N
        FilterB.setToolTipText("Filter Graph");
        FilterB.setPreferredSize(new java.awt.Dimension(70, 70));
        FilterB.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
                revRange.setVisible(true);
                revRange.rangeE.setValue((revisionObjects.get(revisionObjects.size() - 1)).getRevision());
            }
        });

        FullB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ifs/RnD/Resources/maximizeedited.jpg")));
        FullB.setToolTipText("Full Graph");
        FullB.setPreferredSize(new java.awt.Dimension(70, 70));
        FullB.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(ActionEvent e) {
                fullGraph();
            }
        });

        HelpB.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ifs/RnD/Resources/help.png")));
        HelpB.setToolTipText("Help");
        HelpB.setPreferredSize(new java.awt.Dimension(70, 70));
        HelpB.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent evt) {
            }
        });

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);

        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
                jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(jPanel4Layout.createSequentialGroup().addGap(40, 40, 40).addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING).addComponent(HelpB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) //
                .addComponent(ExpandB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(CompressB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(FilterB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addComponent(FullB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)).addContainerGap(60, Short.MAX_VALUE)));
        jPanel4Layout.setVerticalGroup(
                jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(jPanel4Layout.createSequentialGroup().addContainerGap() //
                .addComponent(ExpandB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(CompressB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(FilterB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).
                addComponent(FullB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).
                addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).
                addComponent(HelpB, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).
                addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)));



        add(jPanel4, BorderLayout.WEST);
        statusbar = new javax.swing.JLabel("   Node URL : ");
        statusbar.setPreferredSize(new Dimension(-1, 22));
        statusbar.setBorder(LineBorder.createGrayLineBorder());
        add(statusbar, BorderLayout.SOUTH);
        bText.setBounds(100, 50, 100, 25);
        toolBar1.setBackground(new Color(255, 255, 255));
        toolBar1.addSeparator();
        toolBar1.setSize(xSize, 50);
        toolBar1.add(bURL);
        toolBar1.add(bText);
        Line2D line;
        Stroke drawingStroke = new BasicStroke(3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0);

    }

    /**
     *
     * @param revisionObjects
     * @param repoHis
     * @param rootUrl
     * @param recUrl
     * @param tagObjects
     */
    public RevisionGraph(ArrayList revisionObjects, DisplayReposiroryHistory repoHis, String rootUrl, String recUrl, boolean filter) {

        revRange = new RevRange(this);
        this.rootUrl = rootUrl;
        this.recUrl1 = recUrl;
        this.repoHis = repoHis;
        this.revisionObjects = revisionObjects;
        initComponents();
        i = 0;
        area = new Dimension(0, 0);

        // creating a new drawing pane to draw revision graph
        drawingPane = new DrawingPane();
        drawingPane.setBackground(new Color(255, 255, 255));
        drawingPane.setBounds(0, 0, 250, 250);
        drawingPane.setLocation(250, 100);

        drawingPane.setLayout(null);
        drawingPane.setBorder(new EmptyBorder(new Insets(50, 50, 50, 50)));

        Toolkit tk = Toolkit.getDefaultToolkit();
        xSize = ((int) tk.getScreenSize().getWidth()) - 25;
        ySize = ((int) tk.getScreenSize().getHeight()) - 150;

        //Put the drawing area in a scroll pane.
        JScrollPane scroller = new JScrollPane(drawingPane);
        scroller.setPreferredSize(new Dimension(xSize, ySize));
        add(scroller, null);
        getContentPane().add(toolBar1, BorderLayout.NORTH);
        getContentPane().add(statusbar, BorderLayout.SOUTH);

        bText.setText(recUrl1);

        this.drawing(filter,this.revisionObjects);
    }

    /* Reading a range of revisions by revision number */
    void readRange(int s, int e) {

        ArrayList<Revision> rangeRevOds = new ArrayList<Revision>();
        Iterator<Revision> revObitr = revisionObjects.iterator();

        try {
            while (revObitr.hasNext()) {
                Revision next = revObitr.next();
                if (s <= next.getRevision() & e >= next.getRevision()) {
                    rangeRevOds.add(next);

                }
            }
        } catch (Exception ex) {
            JOptionPane.showMessageDialog(rootPane, "Invalid input", "Error", 0);
        }

        repoHis.revisionGraph.test(rangeRevOds, true, frame);

    }

    /* Draw the graph with all revisions displaying*/
    void fullGraph() {
        repoHis.revisionGraph.test(repoHis.revisionObjects, true, frame);
    }

    /** The component inside the scroll pane. */
    public class DrawingPane extends JPanel {

        private void initComponents() {
        }

        public DrawingPane() {
            initComponents();

        }
        RevVertex V[] = new RevVertex[1500];

        @Override
        public void paintComponent(Graphics g) {

            super.paintComponent(g);
            try {
                nodeUrl.setLocation(50, this.getSize().height - 100);
                nodeUrl.setOpaque(true);
                nodeUrl.setVisible(true);
                drawingPane.add(nodeUrl);

            } catch (NullPointerException nx) {
                System.out.println("picture not found");
            }

            if (tagObjects == null) {

                Iterator<MergeEdge> medgeItr = mergeedgeArrayL.iterator();
                while (medgeItr.hasNext()) {
                    MergeEdge mergeed = medgeItr.next();
                    g.drawLine(mergeed.getStartX(), mergeed.getStartY(), mergeed.getEndX(), mergeed.getEndY());
                }

                for (int i = 0; i < edgeArrayL.size() - 1; i++) {

                    g.drawLine(edgeArrayL.get(i).getStartX(), edgeArrayL.get(i).getStartY(), edgeArrayL.get(i + 1).getStartX(), edgeArrayL.get(i + 1).getStartY());


                }
            }

            if (tagObjects != null) {
                Iterator<MergeEdge> medgeItr = mergeedgeArrayL.iterator();
                while (medgeItr.hasNext()) {
                    MergeEdge mergeed = medgeItr.next();
                    g.drawLine(mergeed.getStartX(), mergeed.getStartY(), mergeed.getEndX(), mergeed.getEndY());

                }


                for (int i = 0; i < edgeArrayL.size() - 1; i++) {

                    g.drawLine(edgeArrayL.get(i).getStartX(), edgeArrayL.get(i).getStartY(), edgeArrayL.get(i + 1).getStartX(), edgeArrayL.get(i + 1).getStartY());

                }
            }
        }
    }

    /**
     * creating revision nodes and edges connecting them
     */
    public void drawing(boolean compress,ArrayList<Revision> revisionObjects) {

        r=100;
        if (revisionObjects.size() > 10 && compress == true) {
            drawCompressed(revisionObjects);

        } else {
            ny = this.getHeight() / 2 - 500;
            for (int i = 0; i < revisionObjects.size(); i++) {
                String rNo = String.valueOf(revisionObjects.get(i).getRevision());
                if (revisionObjects.get(i).getType().equals("Merge")) {
                   ny = ny + 100;
                    try {
                         V.add(i, addVertex(revisionObjects.get(i), rNo, r - 75, (ny + 100)));
                    } catch (Exception e) {
                    }
                    edgeArrayL.add(new MergeEdge(r - 50, ny + 325));
                } else {
                    try {
                        V.add(i, addVertex(revisionObjects.get(i), rNo, r, (ny + 100)));
                    } catch (Exception e) {
                    }
                    edgeArrayL.add(new MergeEdge(r + 50, ny + 325));
                }
                checkMerge(revisionObjects.get(i), r - 75, (ny + 100));
                r = r + 100;
            }
            sW1 = this.getWidth();

            if (sW1 >= (r + 200)) {
                x = sW1;

            } else {
                x = r + 200;
            }

            y = (this.getHeight() / 2 - 15 - 50);
            int this_width = (x + W + 2);
            if (this_width > area.width) {
                area.width = this_width;
                changed = true;
            }

            int this_height = y + H + ny + 350;
            if (this_height > area.height) {
                area.height = this_height - 200;
                changed = true;
            }
            if (changed) {
                //Update client's preferred size because
                //the area taken up by the graphics has
                //gotten larger or smaller (if cleared).
                drawingPane.setPreferredSize(area);
                //Let the scroll pane know to update itself
                //and its scrollbars.
                drawingPane.revalidate();
            }
            drawingPane.repaint();
        }
    }

    /* draw the compressed revision graph*/
    void drawCompressed(ArrayList<Revision> revisionObjects) {

        ArrayList<Revision> creatCompresedRevOb = creatCompresedRevOb(revisionObjects);

        ny = this.getHeight() / 2 - 500;
        for (int j = 0; j < creatCompresedRevOb.size(); j++) {
            String rNo = String.valueOf(creatCompresedRevOb.get(j).getRevision());
            if (creatCompresedRevOb.get(j).getType().equals("Merge")) {
                ny = ny + 100;
                try {
                    V.add(j, addVertex(creatCompresedRevOb.get(j), rNo, r - 75, (ny + 100)));
                } catch (Exception e) {
                }
                edgeArrayL.add(new MergeEdge(r - 50, ny + 325));
            } else {
                try {
                    V.add(j, addVertex(creatCompresedRevOb.get(j), rNo, r, (ny + 100)));
                } catch (Exception e) {
                }
                edgeArrayL.add(new MergeEdge(r + 50, ny + 325));
            }
            checkMerge(creatCompresedRevOb.get(j), r - 75, (ny + 100));
            r = r + 100;

        }
        sW1 = this.getWidth();

        if (sW1 >= (r + 200)) {
            x = sW1;

        } else {
            x = r + 200;
        }

        y = (this.getHeight() / 2 - 15 - 50);
        int this_width = (x + W + 2);
        if (this_width > area.width) {
            area.width = this_width;
            changed = true;
        }

        int this_height = y + H + ny + 350;
        if (this_height > area.height) {
            area.height = this_height - 200;
            changed = true;
        }
        if (changed) {
            //Update client's preferred size because
            //the area taken up by the graphics has
            //gotten larger or smaller (if cleared).
            drawingPane.setPreferredSize(area);
            //Let the scroll pane know to update itself
            //and its scrollbars.
            drawingPane.revalidate();
        }
        drawingPane.repaint();
    }

    /* this filters revision objects for the compressed revision graph.
     * Adds starting revision, end revision,mergers,
     * if there are several revisions with the same modification type only starting
     * is represented
     */
    ArrayList<Revision> creatCompresedRevOb(ArrayList<Revision> revisionObs) {

        ArrayList<Revision> CompresedRevOb = new ArrayList<Revision>();
        int revObNo = 0;


        CompresedRevOb.add(revisionObs.get(0));
        CompresedRevOb.add(revisionObs.get(1));

        if (revisionObs.get(1).getType().equalsIgnoreCase(revisionObs.get(2).getType())) {
            CompresedRevOb.add(new Revision("Expand", "< . . . >", 0));

        }
        for (revObNo = 2; revObNo < revisionObs.size() - 1; revObNo++) {

            if (!(revisionObs.get(revObNo).getType()).equals(revisionObs.get(revObNo + 1).getType())) {
                CompresedRevOb.add(revisionObs.get(revObNo));
                if ((revObNo != revisionObs.size() - 1) && ((revisionObs.get(revObNo + 1).getType()).equals(revisionObs.get(revObNo + 2).getType())) && !(revisionObs.get(revObNo).getType().equalsIgnoreCase("Merge"))) {
                    CompresedRevOb.add(new Revision("Expand", "< . . . >", 0));

                }
            }

            if (("Merge").equals(revisionObs.get(revObNo).getType()) && !("Merge").equals(revisionObs.get(revObNo + 1).getType())
                    && (revisionObs.get(revObNo + 1).getType()).equals(revisionObs.get(revObNo + 2).getType())) {
                CompresedRevOb.add(revisionObs.get(revObNo + 1));
                CompresedRevOb.add(new Revision("Expand", "< . . . >", 0));
            }
        }
        CompresedRevOb.add(revisionObs.get(revisionObs.size() - 1));
        return CompresedRevOb;

    }

    /**
     * Checks whether a merge is occurred
     * @param revision
     * @param sx
     * @param sy
     */
    public void checkMerge(Revision revision, int sx, int sy) {
        if (revision.getCopyComponent() != null) {

            // if the revision is a tag set the tag node
            if (revision.getType().equals("Tag")) {
                tag = addVertex(revision, String.valueOf(revision.getRevision()), sx + 75, (int) (sy - 100));

                mergeedgeArrayL.add(new MergeEdge(sx + 100, sy + 225, sx + 100, (int) sy + 100));
            } else {
             // if the revision is moved, copied (merged but not a tag) set the node
                tag = addVertex(revision, revision.getCopyComponent().getRevisionNO(), sx, (int) (sy + 100));

                mergeedgeArrayL.add(new MergeEdge(sx + 25, sy + 200, sx + 25, (int) sy + 300));
            }
        }
    }

    /**
     * adds a new revision vertex JLabel and sets its location
     * @param revOb
     * @param revNo
     * @param dx
     * @param dy
     * @return
     */
    public RevVertex addVertex(Revision revOb, String revNo, int dx, int dy) {

        u = new RevVertex(revOb, revNo, dx, dy, this);

        u.setLocation(dx, dy + 200);
        drawingPane.add(u);
        return u;
    }

    /**
     *
     * @param list
     */
    public void test(ArrayList list, boolean compress, RevisionGraph revGraph) {
        createAndShowGUI(list, compress, revGraph);
    }

    /* enabling the frame visible
     */
    private void createAndShowGUI(ArrayList revob, boolean compress, RevisionGraph revGraph) {

        frame = revGraph;
        drawingPane.removeAll();
        mergeedgeArrayL.clear();
        edgeArrayL.clear();

        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setTitle(rootUrl + ": Revision Graph");
        frame.pack();

        this.drawing(compress,revob);
        frame.setVisible(true);
    }
}
